Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
78.57% |
11 / 14 |
CRAP | |
97.01% |
130 / 134 |
| InMemory | |
0.00% |
0 / 1 |
|
78.57% |
11 / 14 |
35 | |
97.01% |
130 / 134 |
| collect | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
| flushMemory | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
| collectHistograms | |
100.00% |
1 / 1 |
6 | |
100.00% |
42 / 42 |
|||
| internalCollect | |
100.00% |
1 / 1 |
3 | |
100.00% |
17 / 17 |
|||
| updateHistogram | |
100.00% |
1 / 1 |
6 | |
100.00% |
18 / 18 |
|||
| updateGauge | |
100.00% |
1 / 1 |
4 | |
100.00% |
11 / 11 |
|||
| updateCounter | |
0.00% |
0 / 1 |
4.01 | |
90.91% |
10 / 11 |
|||
| histogramBucketValueKey | |
100.00% |
1 / 1 |
1 | |
100.00% |
5 / 5 |
|||
| metaKey | |
100.00% |
1 / 1 |
1 | |
100.00% |
1 / 1 |
|||
| valueKey | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
| metaData | |
100.00% |
1 / 1 |
1 | |
100.00% |
5 / 5 |
|||
| sortSamples | |
100.00% |
1 / 1 |
1 | |
100.00% |
3 / 3 |
|||
| encodeLabelValues | |
0.00% |
0 / 1 |
2.06 | |
75.00% |
3 / 4 |
|||
| decodeLabelValues | |
0.00% |
0 / 1 |
3.21 | |
71.43% |
5 / 7 |
|||
| <?php | |
| namespace Prometheus\Storage; | |
| use Prometheus\MetricFamilySamples; | |
| use RuntimeException; | |
| class InMemory implements Adapter | |
| { | |
| private $counters = []; | |
| private $gauges = []; | |
| private $histograms = []; | |
| /** | |
| * @return MetricFamilySamples[] | |
| */ | |
| public function collect() | |
| { | |
| $metrics = $this->internalCollect($this->counters); | |
| $metrics = array_merge($metrics, $this->internalCollect($this->gauges)); | |
| $metrics = array_merge($metrics, $this->collectHistograms()); | |
| return $metrics; | |
| } | |
| public function flushMemory() | |
| { | |
| $this->counters = []; | |
| $this->gauges = []; | |
| $this->histograms = []; | |
| } | |
| private function collectHistograms() | |
| { | |
| $histograms = []; | |
| foreach ($this->histograms as $histogram) { | |
| $metaData = $histogram['meta']; | |
| $data = [ | |
| 'name' => $metaData['name'], | |
| 'help' => $metaData['help'], | |
| 'type' => $metaData['type'], | |
| 'labelNames' => $metaData['labelNames'], | |
| 'buckets' => $metaData['buckets'] | |
| ]; | |
| // Add the Inf bucket so we can compute it later on | |
| $data['buckets'][] = '+Inf'; | |
| $histogramBuckets = []; | |
| foreach ($histogram['samples'] as $key => $value) { | |
| $parts = explode(':', $key); | |
| $labelValues = $parts[2]; | |
| $bucket = $parts[3]; | |
| // Key by labelValues | |
| $histogramBuckets[$labelValues][$bucket] = $value; | |
| } | |
| // Compute all buckets | |
| $labels = array_keys($histogramBuckets); | |
| sort($labels); | |
| foreach ($labels as $labelValues) { | |
| $acc = 0; | |
| $decodedLabelValues = $this->decodeLabelValues($labelValues); | |
| foreach ($data['buckets'] as $bucket) { | |
| $bucket = (string)$bucket; | |
| if (!isset($histogramBuckets[$labelValues][$bucket])) { | |
| $data['samples'][] = [ | |
| 'name' => $metaData['name'] . '_bucket', | |
| 'labelNames' => ['le'], | |
| 'labelValues' => array_merge($decodedLabelValues, [$bucket]), | |
| 'value' => $acc | |
| ]; | |
| } else { | |
| $acc += $histogramBuckets[$labelValues][$bucket]; | |
| $data['samples'][] = [ | |
| 'name' => $metaData['name'] . '_' . 'bucket', | |
| 'labelNames' => ['le'], | |
| 'labelValues' => array_merge($decodedLabelValues, [$bucket]), | |
| 'value' => $acc | |
| ]; | |
| } | |
| } | |
| // Add the count | |
| $data['samples'][] = [ | |
| 'name' => $metaData['name'] . '_count', | |
| 'labelNames' => [], | |
| 'labelValues' => $decodedLabelValues, | |
| 'value' => $acc | |
| ]; | |
| // Add the sum | |
| $data['samples'][] = [ | |
| 'name' => $metaData['name'] . '_sum', | |
| 'labelNames' => [], | |
| 'labelValues' => $decodedLabelValues, | |
| 'value' => $histogramBuckets[$labelValues]['sum'] | |
| ]; | |
| } | |
| $histograms[] = new MetricFamilySamples($data); | |
| } | |
| return $histograms; | |
| } | |
| private function internalCollect(array $metrics) | |
| { | |
| $result = []; | |
| foreach ($metrics as $metric) { | |
| $metaData = $metric['meta']; | |
| $data = [ | |
| 'name' => $metaData['name'], | |
| 'help' => $metaData['help'], | |
| 'type' => $metaData['type'], | |
| 'labelNames' => $metaData['labelNames'], | |
| ]; | |
| foreach ($metric['samples'] as $key => $value) { | |
| $parts = explode(':', $key); | |
| $labelValues = $parts[2]; | |
| $data['samples'][] = [ | |
| 'name' => $metaData['name'], | |
| 'labelNames' => [], | |
| 'labelValues' => $this->decodeLabelValues($labelValues), | |
| 'value' => $value | |
| ]; | |
| } | |
| $this->sortSamples($data['samples']); | |
| $result[] = new MetricFamilySamples($data); | |
| } | |
| return $result; | |
| } | |
| public function updateHistogram(array $data) | |
| { | |
| // Initialize the sum | |
| $metaKey = $this->metaKey($data); | |
| if (array_key_exists($metaKey, $this->histograms) === false) { | |
| $this->histograms[$metaKey] = [ | |
| 'meta' => $this->metaData($data), | |
| 'samples' => [] | |
| ]; | |
| } | |
| $sumKey = $this->histogramBucketValueKey($data, 'sum'); | |
| if (array_key_exists($sumKey, $this->histograms[$metaKey]['samples']) === false) { | |
| $this->histograms[$metaKey]['samples'][$sumKey] = 0; | |
| } | |
| $this->histograms[$metaKey]['samples'][$sumKey] += $data['value']; | |
| $bucketToIncrease = '+Inf'; | |
| foreach ($data['buckets'] as $bucket) { | |
| if ($data['value'] <= $bucket) { | |
| $bucketToIncrease = $bucket; | |
| break; | |
| } | |
| } | |
| $bucketKey = $this->histogramBucketValueKey($data, $bucketToIncrease); | |
| if (array_key_exists($bucketKey, $this->histograms[$metaKey]['samples']) === false) { | |
| $this->histograms[$metaKey]['samples'][$bucketKey] = 0; | |
| } | |
| $this->histograms[$metaKey]['samples'][$bucketKey] += 1; | |
| } | |
| public function updateGauge(array $data) | |
| { | |
| $metaKey = $this->metaKey($data); | |
| $valueKey = $this->valueKey($data); | |
| if (array_key_exists($metaKey, $this->gauges) === false) { | |
| $this->gauges[$metaKey] = [ | |
| 'meta' => $this->metaData($data), | |
| 'samples' => [] | |
| ]; | |
| } | |
| if (array_key_exists($valueKey, $this->gauges[$metaKey]['samples']) === false) { | |
| $this->gauges[$metaKey]['samples'][$valueKey] = 0; | |
| } | |
| if ($data['command'] === Adapter::COMMAND_SET) { | |
| $this->gauges[$metaKey]['samples'][$valueKey] = $data['value']; | |
| } else { | |
| $this->gauges[$metaKey]['samples'][$valueKey] += $data['value']; | |
| } | |
| } | |
| public function updateCounter(array $data) | |
| { | |
| $metaKey = $this->metaKey($data); | |
| $valueKey = $this->valueKey($data); | |
| if (array_key_exists($metaKey, $this->counters) === false) { | |
| $this->counters[$metaKey] = [ | |
| 'meta' => $this->metaData($data), | |
| 'samples' => [] | |
| ]; | |
| } | |
| if (array_key_exists($valueKey, $this->counters[$metaKey]['samples']) === false) { | |
| $this->counters[$metaKey]['samples'][$valueKey] = 0; | |
| } | |
| if ($data['command'] === Adapter::COMMAND_SET) { | |
| $this->counters[$metaKey]['samples'][$valueKey] = 0; | |
| } else { | |
| $this->counters[$metaKey]['samples'][$valueKey] += $data['value']; | |
| } | |
| } | |
| /** | |
| * @param array $data | |
| * | |
| * @param $bucket | |
| * | |
| * @return string | |
| */ | |
| private function histogramBucketValueKey(array $data, $bucket) | |
| { | |
| return implode(':', [ | |
| $data['type'], | |
| $data['name'], | |
| $this->encodeLabelValues($data['labelValues']), | |
| $bucket | |
| ]); | |
| } | |
| /** | |
| * @param array $data | |
| * | |
| * @return string | |
| */ | |
| private function metaKey(array $data) | |
| { | |
| return implode(':', [$data['type'], $data['name'], 'meta']); | |
| } | |
| /** | |
| * @param array $data | |
| * | |
| * @return string | |
| */ | |
| private function valueKey(array $data) | |
| { | |
| return implode(':', | |
| [$data['type'], $data['name'], $this->encodeLabelValues($data['labelValues']), 'value']); | |
| } | |
| /** | |
| * @param array $data | |
| * | |
| * @return array | |
| */ | |
| private function metaData(array $data) | |
| { | |
| $metricsMetaData = $data; | |
| unset($metricsMetaData['value']); | |
| unset($metricsMetaData['command']); | |
| unset($metricsMetaData['labelValues']); | |
| return $metricsMetaData; | |
| } | |
| private function sortSamples(array &$samples) | |
| { | |
| usort($samples, function ($a, $b) { | |
| return strcmp(implode("", $a['labelValues']), implode("", $b['labelValues'])); | |
| }); | |
| } | |
| /** | |
| * @param array $values | |
| * @return string | |
| * @throws RuntimeException | |
| */ | |
| private function encodeLabelValues(array $values) | |
| { | |
| $json = json_encode($values); | |
| if (false === $json) { | |
| throw new RuntimeException(json_last_error_msg()); | |
| } | |
| return base64_encode($json); | |
| } | |
| /** | |
| * @param string $values | |
| * @return array | |
| * @throws RuntimeException | |
| */ | |
| private function decodeLabelValues($values) | |
| { | |
| $json = base64_decode($values, true); | |
| if (false === $json) { | |
| throw new RuntimeException('Cannot base64 decode label values'); | |
| } | |
| $decodedValues = json_decode($json, true); | |
| if (false === $decodedValues) { | |
| throw new RuntimeException(json_last_error_msg()); | |
| } | |
| return $decodedValues; | |
| } | |
| } |